package com.zyl.pts.utils;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.zyl.pts.config.enums.ResultCode;
import com.zyl.pts.config.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @version v1.0
 * @ProjectName: product_source
 * @Package: com.zyl.pts.utils
 * @ClassName: CommonUtils
 * @Description TODO(一般工具类)
 * @Author 翟玉龙
 * @Date 2024/11/18-15:20
 */
public class CUtil {

    private static final Logger log = LoggerFactory.getLogger(CUtil.class);

    private static final String QRCODE_PATH = "S:\\Java_Boot\\sources\\";

    /*
     * 缓存文件
     */
    private static final Map<String, File> cache = new HashMap<>();

    /**
     * 将log的各种用法进行封装
     */
    public static void logInfo(String msg) {
        log.info(msg);
    }

    public static void logError(String msg) {
        log.error(msg);
    }

    public static void logDebug(String msg) {
        log.debug(msg);
    }

    public static void logWarn(String msg) {
        log.warn(msg);
    }



    /**
     * 将File转为MultipartFile
     * @param file 文件
     * @return {@link MultipartFile }
     * @throws IOException IO异常
     */
    public static MultipartFile convertMultipartFile(File file) throws IOException {
        // 根据传入的file推断type
        String type = "";
        if(file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg")) {
            type = "image/jpeg";
        }
        if(file.getName().endsWith(".png")) {
            type = "image/png";
        }
        if(file.getName().endsWith(".gif")) {
            type = "image/gif";
        }
        if(file.getName().endsWith(".bmp")) {
            type = "image/bmp";
        }
        if(file.getName().endsWith(".pdf")) {
            type = "application/pdf";
        }
        try(FileInputStream in = new FileInputStream(file)) {
            return new MockMultipartFile("file", file.getName(), type, in);
        }
    }


    /**
     * 生成唯一键
     *
     * @param data  数据
     * @param width 宽度
     * @param height 高度
     * @return 唯一键
     */
    private static String generateUniqueKey(Object data, int width, int height) {
        return data.toString() + "_" + width + "_" + height;
    }

    /**
     * 生成QR码
     * @param data     QR码存储的数据
     * @param width    QR码的宽度
     * @param height   QR码的高度
     * @param filePath QR码的存储路径
     * @return {@link File }
     */
    public static <T> File generateQRCode (T data, int width, int height, String filePath) {
        try {
            // 根据数据、宽度和高度生成唯一键
            String key = generateUniqueKey(data, width, height);

            // 构建完整的文件路径
            filePath = QRCODE_PATH + filePath;
            File codeFile = new File(filePath);

            // 检查缓存中是否已有该二维码
            if (cache.containsKey(key)) {
                logInfo("使用缓存的QR码文件: " + cache.get(key).getAbsolutePath());
                return cache.get(key);
            }
            /*
                生成二维码
             */
            Map<EncodeHintType, Object> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 设置错误校正级别
            hints.put(EncodeHintType.MARGIN, 1); // 二维码边距

            QRCodeWriter writer = new QRCodeWriter(); // 二维码生成器
            BitMatrix bitMatrix = writer.encode((String) data, BarcodeFormat.QR_CODE, width, height, hints);

            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, bitMatrix.get(x, y) ? Color.BLACK.getRGB() : Color.WHITE.getRGB());
                }
            }

            try (ImageOutputStream ios = ImageIO.createImageOutputStream(codeFile)) {
                ImageIO.write(image, "png", ios);
            }

            logInfo("QR码已经生成，并保存到： " + filePath);
            cache.put(key, codeFile);
            return codeFile;

        } catch (IOException | WriterException e) {
            logError("生成二维码失败");
            throw new ServiceException(ResultCode.QRCODE_GEN_FAIL);
        }
    }

    /**
     * 格式化当前时间
     * @return {@link String }
     */
    public static String formatLocalDateTime() {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 使用formatter将now转换为指定格式的字符串
        return now.format(formatter);
    }

    /**
     * 生成随机的UUID
     * @return {@link String }
     */
    public static String UUID() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 将List<Map<String, Object>> 转化为泛型Map<String, <T>> 或者对象类
     * 用于基本键值映射关联到指定对象中
     * @param list 列表
     * @param key 键
     * @param clazz 值类型
     * @return Map<String, T>
     * @param <T> 泛型
     */
    public static <T> Map<String, T> listToMapObject(List<Map<String, Object>> list, String key, Class<T> clazz) {
        // 类型转换检查
        if (!clazz.isInstance(list.get(0).get(key))) {
            logInfo("类型转换错误");
            throw new RuntimeException("类型转换错误");
        }
        return list.stream()
               .collect(Collectors.toMap(
                       item -> String.valueOf(item.get(key)),
                       item -> clazz.cast(item.get(key)),
                        (existingValue, newValue) -> existingValue
               ));
    }

    /**
     * 将List<Map<String, Object>> 转化为泛型Map<String, <T>>
     * 用于拆开Map列表
     * @param list 列表
     * @param keyField 键字段
     * @param valueField 值字段
     * @return Map<String, T>
     * @param <T> 泛型
     */
    public static <T> Map<String, T> listToMap(List<Map<String, Object>> list, String keyField, String valueField, Class<T> clazz) {
        // 检查列表是否为空或为空集合
        if (list == null || list.isEmpty()) {
            return Collections.emptyMap();
        }

        // 类型转换检查
        for (Map<String, Object> item : list) {
            if (!CUtil.isInteger(item.get(valueField))) {
                logInfo("类型转换错误");
                throw new IllegalArgumentException("类型转换错误");
            }
        }

        // 转换并收集结果
        return list.stream()
                .collect(Collectors.toMap(
                        item -> String.valueOf(item.get(keyField)),
                        item -> clazz.cast(item.get(valueField)),
                        (existingValue, newValue) -> existingValue
                ));
    }

    /**
     * PageHelper统一处理为PageInfo
     * @param lists 数据集合
     * @param pageSize 每页大小
     * @param pageNum 页码
     * @return {@link PageInfo }<{@link T }>
     */
    public static <T> PageInfo<T> convertPageInfo(List<T> lists, Integer pageSize, Integer pageNum) {
        Page<T> page = PageHelper.startPage(pageNum, pageSize);
        PageInfo<T> pageInfo = new PageInfo<>(lists);
        page.close();
        return pageInfo;
    }


    /**
     * 判断是否为整数
     * @param o 对象
     * @return boolean
     */
    private static boolean isInteger(Object o) {
        try {
            Integer.parseInt(o.toString());
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    public static void CommonException() {
        RuntimeException e = new RuntimeException("当前没有用户登录!");
        logError("获取token失败,{当前用户未登录}" + e);
        throw e;
    }
}
